package com.my.lucky.service;

import com.my.lucky.model.Stock;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 股票横盘检测服务
 */
public class StockSidewaysDetector {

    private static final DecimalFormat df = new DecimalFormat("#.##");

    /**
     * 横盘分析结果
     */
    public static class SidewaysAnalysisResult {
        private boolean isSideways;
        private double volatility;          // 波动率(%)
        private double priceRangeRatio;     // 价格区间比
        private double maDeviation;         // 均线偏离度(%)
        private double bollingerBandWidth;  // 布林带宽度(%)
        private double confidenceScore;     // 置信度(0-1)
        private double trendStrength;       // 趋势强度(-1到1)
        private String code;

        public SidewaysAnalysisResult(boolean isSideways, double volatility, double priceRangeRatio,
                                      double maDeviation, double bollingerBandWidth, double confidenceScore,
                                      double trendStrength) {
            this.isSideways = isSideways;
            this.volatility = volatility;
            this.priceRangeRatio = priceRangeRatio;
            this.maDeviation = maDeviation;
            this.bollingerBandWidth = bollingerBandWidth;
            this.confidenceScore = confidenceScore;
            this.trendStrength = trendStrength;
        }

        // Getters
        public boolean isSideways() { return isSideways; }
        public double getVolatility() { return volatility; }
        public double getPriceRangeRatio() { return priceRangeRatio; }
        public double getMaDeviation() { return maDeviation; }
        public double getBollingerBandWidth() { return bollingerBandWidth; }
        public double getConfidenceScore() { return confidenceScore; }
        public double getTrendStrength() { return trendStrength; }

        @Override
        public String toString() {
            return "横盘分析结果:\n" +
                    "是否横盘: " + isSideways + "\n" +
                    "波动率: " + df.format(volatility) + "%\n" +
                    "价格区间比: " + df.format(priceRangeRatio) + "\n" +
                    "均线偏离度: " + df.format(maDeviation) + "%\n" +
                    "布林带宽度: " + df.format(bollingerBandWidth) + "%\n" +
                    "置信度: " + df.format(confidenceScore * 100) + "%\n" +
                    "趋势强度: " + df.format(trendStrength);
        }
    }

    /**
     * 检测股票是否处于横盘状态
     * @param stocks 股票数据列表
     * @param startDate 开始日期
     * @param endDate 结束日期
//     * @param volatilityThreshold 波动率阈值(%)
//     * @param rangeThreshold 价格区间阈值
     * @return 分析结果
     */
    public static SidewaysAnalysisResult detectSideways(List<Stock> stocks, LocalDate startDate,
                                                        LocalDate endDate) {
        if (stocks == null || stocks.isEmpty()) {
            throw new IllegalArgumentException("股票数据不能为空");
        }
        double volatilityThreshold=2.0;
        double rangeThreshold=0.2;

        // 过滤指定日期范围内的数据
        List<Stock> filteredStocks = stocks.stream()
                .filter(stock -> !stock.getTradeDate().isBefore(startDate) &&
                        !stock.getTradeDate().isAfter(endDate))
                .sorted(Comparator.comparing(Stock::getTradeDate))
                .collect(Collectors.toList());

        if (filteredStocks.size() < 10) { // 需要至少10个交易日数据
            return null;
//            throw new IllegalArgumentException("数据不足，需要至少10个交易日的数据");
        }

        // 计算各项指标
        double volatility = calculateVolatility(filteredStocks);
        double priceRangeRatio = calculatePriceRangeRatio(filteredStocks);
        double maDeviation = calculateMovingAverageDeviation(filteredStocks);
        double bollingerBandWidth = calculateBollingerBandWidth(filteredStocks);
        double trendStrength = calculateTrendStrength(filteredStocks);

        // 综合判断
        boolean isSideways = isSidewaysMarket(volatility, priceRangeRatio, maDeviation,
                bollingerBandWidth, trendStrength,
                volatilityThreshold, rangeThreshold);

        // 计算置信度
        double confidenceScore = calculateConfidenceScore(volatility, priceRangeRatio,
                maDeviation, bollingerBandWidth,
                trendStrength, volatilityThreshold, rangeThreshold);

        return new SidewaysAnalysisResult(isSideways, volatility, priceRangeRatio,
                maDeviation, bollingerBandWidth, confidenceScore,
                trendStrength);
    }

    /**
     * 计算波动率
     * @param stocks 股票数据
     * @return 波动率(%)
     */
    private static double calculateVolatility(List<Stock> stocks) {
        List<Double> returns = new ArrayList<>();

        for (int i = 1; i < stocks.size(); i++) {
            double prevClose = stocks.get(i - 1).getForwardAdjustedClose();
            double currentClose = stocks.get(i).getForwardAdjustedClose();
            double dailyReturn = (currentClose - prevClose) / prevClose;
            returns.add(dailyReturn);
        }

        double mean = returns.stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
        double variance = returns.stream()
                .mapToDouble(r -> Math.pow(r - mean, 2))
                .average()
                .orElse(0.0);

        double stdDev = Math.sqrt(variance);
        return stdDev * 100; // 转换为百分比
    }

    /**
     * 计算价格区间比
     * @param stocks 股票数据
     * @return 价格区间比
     */
    private static double calculatePriceRangeRatio(List<Stock> stocks) {
        double maxHigh = stocks.stream()
                .mapToDouble(Stock::getForwardAdjustedHigh)
                .max()
                .orElse(0.0);

        double minLow = stocks.stream()
                .mapToDouble(Stock::getForwardAdjustedLow)
                .min()
                .orElse(0.0);

        return (maxHigh - minLow) / minLow;
    }

    /**
     * 计算移动平均线偏离度
     * @param stocks 股票数据
     * @return 偏离度(%)
     */
    private static double calculateMovingAverageDeviation(List<Stock> stocks) {
        int period = stocks.size();

        double sumClose = stocks.stream()
                .mapToDouble(Stock::getForwardAdjustedClose)
                .sum();

        double ma = sumClose / period;

        double deviations = 0.0;
        for (Stock stock : stocks) {
            deviations += Math.abs(stock.getForwardAdjustedClose() - ma);
        }

        double avgDeviation = deviations / period;
        return (avgDeviation / ma) * 100; // 转换为百分比
    }

    /**
     * 计算布林带宽度
     * @param stocks 股票数据
     * @return 布林带宽度(%)
     */
    private static double calculateBollingerBandWidth(List<Stock> stocks) {
        int period = stocks.size();

        // 使用已计算好的布林带数据
        double avgBollMid = stocks.stream()
                .mapToDouble(Stock::getBollMid)
                .average()
                .orElse(0.0);

        double avgBollUpper = stocks.stream()
                .mapToDouble(Stock::getBollUpper)
                .average()
                .orElse(0.0);

        double avgBollLower = stocks.stream()
                .mapToDouble(Stock::getBollLower)
                .average()
                .orElse(0.0);

        return ((avgBollUpper - avgBollLower) / avgBollMid) * 100; // 转换为百分比
    }

    /**
     * 计算趋势强度
     * @param stocks 股票数据
     * @return 趋势强度(-1到1)，0表示横盘
     */
    private static double calculateTrendStrength(List<Stock> stocks) {
        if (stocks.size() < 2) {
            return 0.0;
        }

        // 简单线性回归计算趋势
        int n = stocks.size();
        double sumX = 0.0;
        double sumY = 0.0;
        double sumXY = 0.0;
        double sumX2 = 0.0;

        for (int i = 0; i < n; i++) {
            double x = i;
            double y = stocks.get(i).getForwardAdjustedClose();

            sumX += x;
            sumY += y;
            sumXY += x * y;
            sumX2 += x * x;
        }

        double numerator = n * sumXY - sumX * sumY;
        double denominator = n * sumX2 - sumX * sumX;

        if (denominator == 0) {
            return 0.0;
        }

        double slope = numerator / denominator;

        // 归一化趋势强度
        double maxPossibleSlope = (stocks.get(stocks.size() - 1).getForwardAdjustedClose() -
                stocks.get(0).getForwardAdjustedClose()) / stocks.size();

        if (maxPossibleSlope == 0) {
            return 0.0;
        }

        double trendStrength = slope / Math.abs(maxPossibleSlope);

        // 限制在-1到1之间
        return Math.max(-1.0, Math.min(1.0, trendStrength));
    }

    /**
     * 判断是否横盘
     * @param volatility 波动率
     * @param priceRangeRatio 价格区间比
     * @param maDeviation 均线偏离度
     * @param bollingerBandWidth 布林带宽度
     * @param trendStrength 趋势强度
     * @param volatilityThreshold 波动率阈值
     * @param rangeThreshold 价格区间阈值
     * @return 是否横盘
     */
    private static boolean isSidewaysMarket(double volatility, double priceRangeRatio,
                                            double maDeviation, double bollingerBandWidth,
                                            double trendStrength, double volatilityThreshold,
                                            double rangeThreshold) {
        // 所有指标都满足条件才认为是横盘
        boolean volatilityCondition = volatility < volatilityThreshold;
        boolean rangeCondition = priceRangeRatio < rangeThreshold;
        boolean maCondition = maDeviation < volatilityThreshold;
        boolean bollingerCondition = bollingerBandWidth < rangeThreshold * 100;
        boolean trendCondition = Math.abs(trendStrength) < 0.3; // 趋势强度接近0

        return volatilityCondition && rangeCondition && maCondition && bollingerCondition && trendCondition;
    }

    /**
     * 计算置信度
     * @return 置信度(0-1)
     */
    private static double calculateConfidenceScore(double volatility, double priceRangeRatio,
                                                   double maDeviation, double bollingerBandWidth,
                                                   double trendStrength, double volatilityThreshold,
                                                   double rangeThreshold) {
        double score = 0.0;

        // 波动率得分
        double volatilityScore = Math.max(0, 1 - volatility / volatilityThreshold);
        score += volatilityScore * 0.25;

        // 价格区间得分
        double rangeScore = Math.max(0, 1 - priceRangeRatio / rangeThreshold);
        score += rangeScore * 0.25;

        // 均线偏离度得分
        double maScore = Math.max(0, 1 - maDeviation / volatilityThreshold);
        score += maScore * 0.20;

        // 布林带得分
        double bollingerScore = Math.max(0, 1 - bollingerBandWidth / (rangeThreshold * 100));
        score += bollingerScore * 0.20;

        // 趋势强度得分
        double trendScore = 1 - Math.abs(trendStrength);
        score += trendScore * 0.10;

        return score;
    }

    /**
     * 批量检测多只股票的横盘状态
     * @param stocksByCode 按股票代码分组的股票数据
     * @param startDate 开始日期
     * @param endDate 结束日期
     * @param volatilityThreshold 波动率阈值
     * @param rangeThreshold 价格区间阈值
     * @return 检测结果映射
     */
    public static Map<String, SidewaysAnalysisResult> batchDetectSideways(
            Map<String, List<Stock>> stocksByCode, LocalDate startDate, LocalDate endDate,
            double volatilityThreshold, double rangeThreshold) {

        Map<String, SidewaysAnalysisResult> results = new HashMap<>();

        for (Map.Entry<String, List<Stock>> entry : stocksByCode.entrySet()) {
            try {
                SidewaysAnalysisResult result = detectSideways(
                        entry.getValue(), startDate, endDate);
                results.put(entry.getKey(), result);
            } catch (Exception e) {
                System.err.println("检测股票 " + entry.getKey() + " 失败: " + e.getMessage());
            }
        }

        return results;
    }

    /**
     * 按横盘强度排序股票
     * @param results 检测结果
     * @return 排序后的股票列表
     */
    public static List<Map.Entry<String, SidewaysAnalysisResult>> sortStocksBySidewaysStrength(
            Map<String, SidewaysAnalysisResult> results) {

        return results.entrySet().stream()
                .sorted((e1, e2) -> Double.compare(e2.getValue().getConfidenceScore(),
                        e1.getValue().getConfidenceScore()))
                .collect(Collectors.toList());
    }

    /**
     * 生成横盘检测报告
     * @param results 检测结果
     * @param startDate 开始日期
     * @param endDate 结束日期
     * @return 检测报告
     */
    public static String generateSidewaysReport(Map<String, SidewaysAnalysisResult> results,
                                                LocalDate startDate, LocalDate endDate) {

        StringBuilder report = new StringBuilder();
        report.append("=== 股票横盘检测报告 ===\n");
        report.append("检测期间: ").append(startDate).append(" 至 ").append(endDate).append("\n");
        report.append("检测股票数量: ").append(results.size()).append("\n");

        long sidewaysCount = results.values().stream()
                .filter(SidewaysAnalysisResult::isSideways)
                .count();

        report.append("横盘股票数量: ").append(sidewaysCount).append("\n");
        report.append("横盘股票比例: ").append(df.format((double) sidewaysCount / results.size() * 100)).append("%\n\n");

        report.append("横盘股票详情:\n");
        results.entrySet().stream()
                .filter(entry -> entry.getValue().isSideways())
                .sorted((e1, e2) -> Double.compare(e2.getValue().getConfidenceScore(),
                        e1.getValue().getConfidenceScore()))
                .forEach(entry -> {
                    SidewaysAnalysisResult result = entry.getValue();
                    report.append("股票代码: ").append(entry.getKey()).append("\n");
                    report.append("置信度: ").append(df.format(result.getConfidenceScore() * 100)).append("%\n");
                    report.append("波动率: ").append(df.format(result.getVolatility())).append("%\n");
                    report.append("价格区间比: ").append(df.format(result.getPriceRangeRatio())).append("\n");
                    report.append("趋势强度: ").append(df.format(result.getTrendStrength())).append("\n\n");
                });

        return report.toString();
    }

    /**
     * 主方法用于演示
     */
    public static void main(String[] args) {
        try {
            System.out.println("=== 股票横盘检测工具 ===");

//            // 获取股票数据
//            List<Stock> stocks = stockRepository.findByCodeAndTradeDateBetween(
//                    "600000", LocalDate.of(2024, 1, 1), LocalDate.of(2024, 3, 31));
//
//// 检测横盘
//            SidewaysAnalysisResult result = StockSidewaysDetector.detectSideways(
//                    stocks, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 3, 31),
//                    2.0, 0.05);
//            System.out.println(result);

            // 这里假设你已经从数据库获取了股票数据
            // List<Stock> stocks = stockRepository.findByCodeAndTradeDateBetween("600000", startDate, endDate);

            System.out.println("使用说明:");
            System.out.println("1. 推荐参数设置:");
            System.out.println("   - 检测周期: 20-60个交易日");
            System.out.println("   - 波动率阈值: 1.5%-3.0%");
            System.out.println("   - 价格区间阈值: 0.03-0.10");
            System.out.println("   - 置信度>0.7时判断为强横盘");

            System.out.println("\n2. 调用示例:");
            System.out.println("   LocalDate startDate = LocalDate.of(2024, 1, 1);");
            System.out.println("   LocalDate endDate = LocalDate.of(2024, 3, 31);");
            System.out.println("   SidewaysAnalysisResult result = StockSidewaysDetector.detectSideways(");
            System.out.println("       stocks, startDate, endDate, 2.0, 0.05);");
            System.out.println("   System.out.println(result);");

        } catch (Exception e) {
            System.err.println("程序执行错误: " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 判断股票价格的高低价差是否在最低价的20%以内
     * @param stocks 股票数据列表
     * @return 高低价差分析结果
     */
    public static PriceRangeAnalysisResult isHighLowRangeWithin20Percent(List<Stock> stocks) {
        if (stocks == null || stocks.isEmpty()) {
            throw new IllegalArgumentException("股票数据不能为空");
        }

        // 计算整个时间段的最高价和最低价
        double maxHigh = stocks.stream()
                .mapToDouble(Stock::getForwardAdjustedHigh)
                .max()
                .orElse(0.0);

        double minLow = stocks.stream()
                .mapToDouble(Stock::getForwardAdjustedLow)
                .min()
                .orElse(0.0);

        if (minLow <= 0) {
            throw new IllegalArgumentException("最低价不能为0或负数");
        }

        // 计算价格差和百分比
        double priceDifference = maxHigh - minLow;
        double priceRangePercentage = (priceDifference / minLow) * 100;
        boolean isWithin20Percent = priceRangePercentage <= 20;

        return new PriceRangeAnalysisResult(
                isWithin20Percent, maxHigh, minLow, priceDifference, priceRangePercentage);
    }

    /**
     * 价格区间分析结果
     */
    public static class PriceRangeAnalysisResult {
        private boolean isWithin20Percent;
        private double maxHigh;
        private double minLow;
        private double priceDifference;
        private double priceRangePercentage;

        public PriceRangeAnalysisResult(boolean isWithin20Percent, double maxHigh, double minLow,
                                        double priceDifference, double priceRangePercentage) {
            this.isWithin20Percent = isWithin20Percent;
            this.maxHigh = maxHigh;
            this.minLow = minLow;
            this.priceDifference = priceDifference;
            this.priceRangePercentage = priceRangePercentage;
        }

        // Getters
        public boolean isWithin20Percent() { return isWithin20Percent; }
        public double getMaxHigh() { return maxHigh; }
        public double getMinLow() { return minLow; }
        public double getPriceDifference() { return priceDifference; }
        public double getPriceRangePercentage() { return priceRangePercentage; }

        @Override
        public String toString() {
            return "价格区间分析结果:\n" +
                    "最高价: " + df.format(maxHigh) + "\n" +
                    "最低价: " + df.format(minLow) + "\n" +
                    "价格差: " + df.format(priceDifference) + "\n" +
                    "价格差占最低价百分比: " + df.format(priceRangePercentage) + "%\n" +
                    "是否在20%以内: " + isWithin20Percent;
        }
    }
}